上篇只介紹到最基本的使用者互動操作,本篇的 Pan
、 whileDrag
具有持續的互動性,在過程中能依照更細節的偏移量 (offset)、速度 (velocity)、座標位置 (point) 來變換動畫。最後還有 whileInView
是我覺得最好用的,scroll trigger 幾乎是每個網頁都會添加的動畫,再調整一些動畫參數,就能獲得不一樣的感受。
本節資源 :
在介紹 Drag 之前,要來討論 Pan。在攝影技巧裡是稱作固定鏡頭的平移,在網頁上是 使用者在固定區塊進行偏移。
跟前一篇提到的 Tap 差別在,只要 點擊之後移動超過 3 像素,就啟動 Pan,從點擊滑動超過 3 像素到放開點擊,這是一個完整的 Pan。
Pan 跟 Tap 通常會是一組,並且要符合 CSS touch-action 的規則 (應用於觸控式螢幕), 必須禁用 x/y 軸的移動。
Pan 也提供在不同時間點的事件監聽 onPanStart
與 onPanEnd
<motion.div
onPan={(e, info) => {{ ... }}
onPanStart={(e, info) => { ... }}
onPanEnd={(e, info) => { ... }}
/>
效果 : (待補)
拖曳 (drag) 可以為網頁添加不少趣味性,whileDrag
就是 正在拖曳的動畫狀態,motion 元件還有提供其他 drag 的 props 快速設定 drag 更細節上的操作 :
drag
: 給予拖曳權限與拖曳的方向,共有三個值boolean
: 如果是 true
兩個方向都可以拖曳,反之 false
就是不能拖曳。x
: 只能平移。y
: 只能垂直。dragConstraints
: 拖曳的範圍限制,可以侷限在指定的上下左右 (top, bottom, left & right) 或是搭配 useRef
的 ref
限制在父元素容器內。跟其他互動一樣,也有提供更細微操作的監聽事件 onDrag
、onDragStart
與 onDragEnd
<motion.div
drag="x"
dragConstraints={ref}
whileDrag={{
scale: 1.5,
cursor: "grabbing",
}}
onDrag={(e, info) => {{ ... }}
onDragEnd={(e, info) => { ... }}
/>
拖曳在專案中很常用來做物件排序,在 motion 裡有專屬的 Component 處理這件事,之後會再提到,暫時以單個元素的拖曳為範例。
input
的 range slider 是最常看見的拖曳操作
.slider{
position: relative;
width: 300px;
height: 10px;
border-radius: 10px;
background: #aaa;
display: flex;
align-items: center;
border: 1px solid #666;
}
.slider::after{
position: absolute;
top:0;
bottom:0;
left:0;
right: 0;
background-color: #aaa;
}
.slider-thumb{
display: block;
width: 30px;
height: 30px;
cursor: grab;
line-height: 30px;
text-align: center;
font-size: 30px;
margin-left: -6px;
text-shadow: 0 3px rgba(0,0,0,.3);
}
const faces = ["(疑惑)", "(生氣)", "(尷尬)", "(無言)", "(開心)", "(興奮)", "(大愛)"];
const colors = ["#aaa", "#f00", "#0ea", "#0aa", "#fa2", "#fe2", "#fae"];
// 轉換成對應的 index,由於我設定的 slider width 是 300 分成多個層級
function pointer2Range(pointerX) {
const number = Math.round(pointerX / 60);
if (number <= 1) return 1;
if (number >= 6) return 6;
return number;
}
const sliderRef = useRef(null);
const [range, setRange] = useState(0);
<motion.div
className="slider"
{/* 拿到元素的資料˙ */}
ref={sliderRef}
{/* 改變背景顏色 */}
animate={{
background: colors[range],
}}
>
<motion.span
className="slider-thumb"
drag="x"
{/* 限制拖曳的最大範圍*/}
dragConstraints={sliderRef}
{/* 按住拖曳 */}
whileDrag={{
scale: 1.5,
cursor: "grabbing",
}}
{/* 滑動中 */}
onDrag={(e, info) => {
setRange(pointer2Range(info.point.x));
}}
{/* 結束滑動,放開點擊或滑鼠 */}
onDragEnd={(e, info) => {
setRange(pointer2Range(info.point.x));
}}
{/* 在滑動時就會替換臉 */}
{faces[range]}
</motion.span>
</motion.div>
這是我覺得超好用的屬性,如果你曾用過 AOS 套件,這個就不會很陌生。
inView
顧名思義就是 當元素進入 viewport 觸發的動畫。以往要透過 Intersection Observer API
設定,現在搭配 motion props 就能簡單地讓網頁看見後滑動飛入的效果。
通常搭配 initial
,起始位置先設定好後,等進入 viewport 在啟動動畫
<motion.div
initial={{
x: -100
}}
whileInView={{
x : 0
}}
/>
今天只會講到最基本的 scroll 屬性,之後會再實作比較 fancy 的動畫,如果迫不及待,可以看
官方的 scroll 範例,裡面都很實用,舉凡 : 跑馬燈、頁面進度條等等的。
預設是不再 view 裡回歸到 initial
動畫,進入就執行 whileInView
,要是 想要只出現 1 次,只要加上 viewport
的 once :
viewport={{ once: true }}
效果 : (待補)
原生 CSS 也有個神祕的 scroll trigger 叫 @scroll-timeline ,目前是棄用狀態 QQ ,或許某朝一日會再復活也說不定,實際的例子可以看 CSS-tricks 的文章。
onPan
: 固定元素的位移事件,其他還有 onPanStart
與 onPanEnd
whileDrag
: 拖曳當下觸發的動畫,事件相關有 onDrag
、onDragStart
與 onDragEnd
whileInView
: 當元素進入 viewport 啟動動畫大致上基本動畫的 props 都走過一輪了,使用上就是考驗大家的創意。明天會討論 motion 中強大的變體 Variants ,是一個把動畫個別提出 props,除了提高元件的重複性之外,也可以在細微調整 initial
與 animate
個別的 transition
屬性 。
我發現 IT 邦的文章編輯器不能辨識 emoji ... ,導致使用到的部分都變問號 QQ ,因此突然看到問號不是我在自我懷疑,我會再修正。